iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 20
0

文同步分享於個人blog

  • 定義


運用共享技術有效地支持大量細粒度的物件。

享元模式是運用共享技術有效地支持大量細粒度的物件。他可以避免大量相似類的開銷,在軟體開發中如果需要生成大量細粒度的類別實體來表示數據,如果這些實體除了幾個參數外基本上都是相同的,這時候就可以使用享元模式來大幅度減少需要實體化類的數量。如果能把這些參數(指的這些類別實體不同的參數)移動類別實體外面,在方法調用時將他們傳遞進來,這樣就可以通過共享大幅度地減少單個實體的數目(這個也是享元模式的實現要領)。然而我們把類實體外面的參數稱為享元物件的外部狀態,把在享元物件內部定義稱為內部狀態。

  • 內部狀態 v.s. 外部狀態


內部狀態

不會隨著環境改變而產生變化,所以內部狀態是可以共享的部分。

外部狀態

會隨著環境改變而產生變化的部分,故這部分不可共享。

享元物件的外部狀態必須由Client保存。享元物件建立之後,在需要使用時將其傳入享元物件的內部。外部狀態之間是互相獨立的。

  • Flyweight Pattern 成員


成員 功用
Flyweight 為介面或抽象類別的接口,其中定義ConcreteFlyweight的方法,非享元的外部狀態以參數的形式通過方法傳入。
ConcreteFlyweight 實現Flyweight
UnsharableFlyweight 稱作複合享元物件,不可共享。但可分解成多個單純享元物件,則可共享。
FlyweightFactory 負責建立及管理享元物件。當Client呼叫享元物件時,FlyweightFactory檢查是否存在符合要求的物件,存在則提供,不存在建立新的享元物件。

F1

  • 單純享元模式 v.s. 複合享元模式


剛剛在介紹成員時有提到可以共享與不可以共享的部分,因為這樣,在實作中享元模式又分成單純享元模式以及複合享元模式。

單純享元模式

所有實體享元物件都是可以共享的,不存在UnsharableFlyweight。

F2

複合享元模式

將單純享元物件使用組合形成複合享元物件,此物件不可共享。但若將其分解成單純享元物件,則可以。複合享元物件可對多個單純享元物件設定相同的外部狀態。

F3

  • Flyweight Pattern 實作


看完了一堆定義以及解釋,現在來實作看看。在一間公司內,在錄取之後都會有員工資訊,也會有名片。假設名片是個類別,分別需要CTO、CEO以及COO的名片。而每個名片的實體,也只有資訊不一樣而已,這時就可以套用享元模式。

單純享元模式

首先先建立抽象享元,將方法先定義好。而show()傳入的參數則是外部狀態。

//抽象享元角色
interface NameCard {
    // 外部狀態 msg
    public void show(String msg);
}

而實體享元角色則實作抽象享元,在實體享元內的參數key則是可以共用的內部狀態。

//實體享元角色
class ConcreteNameCard implements NameCard {
    // 內部狀態key
    private String key;
    ConcreteNameCard(String key) {
        this.key=key;
        System.out.println("Create " + key + "NameCard!!");
    }
    public void show(String msg) {
        System.out.print("Hello!! I am " + key + ". ");
        System.out.println(msg);
    }
}

享元工廠內則管理各種享元元件。

//享元工廠角色
class NameCardFactory {
    private static NameCardFactory FACTORY = new NameCardFactory();
    private HashMap<String, NameCard> nameCards=new HashMap<String, NameCard>();
    public static NameCardFactory getInstance() {
        return FACTORY;
    }
    public NameCard getNameCard(String key) {
        NameCard nameCard = (NameCard)nameCards.get(key);
        if(nameCard != null) {
            System.out.println("ConcreteNameCard " + key + " is exist!!");
        }
        else {
            nameCard = new ConcreteNameCard(key);
            nameCards.put(key, nameCard);
        }
        return nameCard;
    }
}

最後Client經由享元工廠來使用享元元件。

public class FlyweightPattern {
    public static void main(String[] args) {
        NameCardFactory factory = new NameCardFactory();
        NameCard ceo = factory.getNameCard("CEO");       
        ceo.show("This is my NameCard");
        NameCard ceo2 = factory.getNameCard("CEO");
        ceo2.show("This is my NameCard");
        NameCard coo = factory.getNameCard("COO");
        coo.show("Nice to meet you!!");
        
    }
}

output

Create CEONameCard!!
Hello!! I am CEO. This is my NameCard
ConcreteNameCard CEO is exist!!
Hello!! I am CEO. This is my NameCard
Create COONameCard!!
Hello!! I am COO. Nice to meet you!!

複合享元模式

複合享元模式的抽象享元以及實體享元和單純享元一樣。

//抽象享元角色
interface NameCard {
    // 外部狀態 msg
    public void show(String msg);
}
//實體享元角色
class ConcreteNameCard implements NameCard {
    //內部狀態 key
    private String key;
    
    ConcreteNameCard(String key) {
        this.key=key;
        System.out.println("Create " + key + "NameCard!!");
    }
    public void show(String msg) {
        System.out.print("Hello!! I am " + key + ". ");
        System.out.println(msg);
    }
}

複合享元多了一個複合享元角色,他將會由多個單純享元物件組成,不可共享。

//複合實體享元角色
class CompositeConcreteNameCard implements NameCard {
    private Map<String, NameCard> nameCards = new HashMap<>();
    @Override
    public void show(String msg) {
        for (Map.Entry<String, NameCard> entry : nameCards.entrySet()) {
            entry.getValue().show(msg);
        }
    }
    public void add(String key, NameCard nameCard) {
        nameCards.put(key, nameCard);
    }
    public void remove(String type) {
        nameCards.remove(type);
    }
}

享元工廠內也新增了複合享元getNameCard()的方法,傳入參數從String改成List。

//享元工廠角色
class NameCardFactory {
    private static NameCardFactory FACTORY = new NameCardFactory();
    private HashMap<String, NameCard> nameCards=new HashMap<String, NameCard>();
    public static NameCardFactory getInstance() {
        return FACTORY;
    }
    // 單純享元模式
    public NameCard getNameCard(String key) {
        NameCard nameCard = (NameCard)nameCards.get(key);
        if(nameCard != null) {
            System.out.println("ConcreteNameCard " + key + " is exist!!");
        }
        else {
            nameCard = new ConcreteNameCard(key);
            nameCards.put(key, nameCard);
        }
        return nameCard;
    }
    // 複合享元模式
    public NameCard getNameCard(List<String> keyList) {
        CompositeConcreteNameCard nameCard = new CompositeConcreteNameCard();
        for (String key : keyList) {
            nameCard.add(key, this.getNameCard(key));
        }
        return nameCard;
    }
}

Client端先將要建立的名片放到一個List內,然後將這List放到工廠內新建立的getNameCard()內,則可取得名片。而複合享元物件不可以共用,在最後可以看到我們建立一個先的複合享元物件,他並不等於第一個。

public class FlyweightPattern {
    public static void main(String[] args) {
        NameCardFactory factory = new NameCardFactory();
        
        List<String> keyList = new ArrayList<>();
        keyList.add("CTO");
        keyList.add("COO");
        keyList.add("CEO");
        
        NameCard member = factory.getNameCard(keyList);    
        member.show("This is my NameCard");    
        
        NameCard member2 = factory.getNameCard(keyList);   
        // 不相等,複合享元模式的物件不可共享
        System.out.println(member == member2);
    }
}

output

Create CTONameCard!!
Create COONameCard!!
Create CEONameCard!!
Hello!! I am COO. This is my NameCard
Hello!! I am CEO. This is my NameCard
Hello!! I am CTO. This is my NameCard
ConcreteNameCard CTO is exist!!
ConcreteNameCard COO is exist!!
ConcreteNameCard CEO is exist!!
false
  • 小結


Flyweight Pattern的目標

藉由共享來實現重用大量细粒度,減少需要建立的物件數量、避免大量類似物件的創建,進而提高資源使用率。

Flyweight Pattern的成員
  Flyweight:為介面或抽象類別的接口,其中定義ConcreteFlyweight的方法,非享元的外部狀態以參數的形式通過方法傳入。
  ConcreteFlyweight:實體享元物件。
  UnsharableFlyweight:複合享元物件,不可共享。可分解成多個單純享元物件,則可共享。
  FlyweightFactory:負責建立及管理享元物件。當Client呼叫享元物件時,FlyweightFactory檢查是否存在符合要求的物件,存在則提供,不存在建立新的享元物件。
Flyweight Pattern的優缺點
優點
1. 相同的物件只需要保存一份,降低了系統中的物件數量,也跟著降低系統細顆粒度物件對記憶體的壓力。
2. 享元模式的物件狀態相對是獨立的,不會影響到內部狀態,讓享元物件可在不同的環境被共享,。
缺點
1. 享元模式需要分離內部及外部狀態,這讓系統邏輯變得複雜化。
2. 為了是可以共享,享元模式需要將享元物件的部分狀態外部化,而讀取外部狀態使得執行時間拉長。
Flyweight Pattern的應用
1. 一個系統中有大量的物件,這些物件耗費大量的記憶體。
2. 大部分物件可照內部狀態分組,且可將不同部分外部化,讓每一組只需保存一個內部狀態。
  • 範例程式碼


範例1:單純享元模式
範例2:複合享元模式

  • References


23种设计模式之享元(Flyweight)模式
享元模式(详解版)
设计模式21---设计模式之享元模式(Flyweight)(结构型)


上一篇
[Day19] 外觀模式 | Facade Pattern
下一篇
[Day21] 代理模式 | Proxy Pattern
系列文
從生活中認識Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言